package com.florent37.runtime_permission;


import ohos.aafwk.ability.fraction.Fraction;
import ohos.aafwk.ability.fraction.FractionAbility;
import ohos.aafwk.content.Intent;
import ohos.app.Context;
import ohos.app.dispatcher.TaskDispatcher;
import ohos.sysappcomponents.settings.SystemSettings;
import ohos.utils.IntentConstants;
import ohos.utils.net.Uri;

import java.lang.ref.Reference;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Optional;

import com.florent37.runtime_permission.callbacks.*;

import static ohos.bundle.IBundleManager.*;

public class RuntimePermission {

    private static final String TAG = "PERMISSION_FRAGMENT_WEEEEE";

    private final Reference<FractionAbility> activityReference;

    //The list of permissions we want to ask
    private final List<String> permissionsToRequest = new ArrayList<>();

    //region callbacks
    private final List<ResponseCallback> responseCallbacks = new ArrayList<>();
    private final List<AcceptedCallback> acceptedCallbacks = new ArrayList<>();
    private final List<ForeverDeniedCallback> foreverDeniedCallbacks = new ArrayList<>();
    private final List<DeniedCallback> deniedCallbacks = new ArrayList<>();
    private final List<PermissionListener> permissionListeners = new ArrayList<>();

    //the listener we will give to the fragment
    private final PermissionFraction.PermissionListener listener = new PermissionFraction.PermissionListener() {
        @Override
        public void onRequestPermissionsResult(List<String> acceptedPermissions, List<String> refusedPermissions, List<String> askAgainPermissions) {
            onReceivedPermissionResult(acceptedPermissions, refusedPermissions, askAgainPermissions);
        }
    };
    //endregion



    public RuntimePermission(final FractionAbility activity) {
        if (activity != null) {
            this.activityReference = new WeakReference<>(activity);
        } else {
            this.activityReference = new WeakReference<>(null);
        }
    }

    /**
     * Fill permissions to only ask If we do not call this method,
     * If not set or empty, the library will find all needed permissions to ask from manifest
     * You can call .request(permissions) after this method if you want to give permissions in a separate method
     */
    public static RuntimePermission askPermission(FractionAbility ability, String... permissions) {
        return new RuntimePermission(ability).request(permissions);
    }

    /**
     * Fill permissions to only ask If we do not call this method,
     * If not set or empty, the library will find all needed permissions to ask from manifest
     * You can call .request(permissions) after this method if you want to give permissions in a separate method
     */
    public static RuntimePermission askPermission(final Fraction fragment, String... permissions) {
        FractionAbility activity = null;
        if (fragment != null) {
            activity = fragment.getFractionAbility();
        }
        return askPermission(activity).request(permissions);
    }

    /**
     * Just a helper methods in case the user blocks permission.
     * It goes to your application settings page for the user to enable permission again.
     */
    public void goToSettings() {
        final FractionAbility fractionAbility = this.activityReference.get();
        if (fractionAbility != null) {
            Intent intent = new Intent();
            intent.setAction(IntentConstants.ACTION_APPLICATION_DETAILS_SETTINGS);
            intent.addFlags(Intent.FLAG_ABILITY_NEW_MISSION);
            intent.setUri(Uri.getUriFromParts("package", fractionAbility.getBundleName(), null));
            fractionAbility.startAbility(intent);
        }
    }

    private void onReceivedPermissionResult(List<String> acceptedPermissions, List<String> refusedPermissions, List<String> askAgainPermissions) {
        final PermissionResult permissionResult = new PermissionResult(this, acceptedPermissions, refusedPermissions, askAgainPermissions);
        if (permissionResult.isAccepted()) {
            for (AcceptedCallback callback : acceptedCallbacks) {
                callback.onAccepted(permissionResult);
            }
            for (PermissionListener permissionListener : permissionListeners) {
                permissionListener.onAccepted(permissionResult, permissionResult.getAccepted());
            }
        }
        if (permissionResult.hasDenied()) {
            for (DeniedCallback callback : deniedCallbacks) {
                callback.onDenied(permissionResult);
            }
        }
        if (permissionResult.hasForeverDenied()) {
            for (ForeverDeniedCallback callback : foreverDeniedCallbacks) {
                callback.onForeverDenied(permissionResult);
            }
        }

        if (permissionResult.hasForeverDenied() || permissionResult.hasDenied()) {
            for (PermissionListener permissionListener : permissionListeners) {
                permissionListener.onDenied(permissionResult, permissionResult.getDenied(), permissionResult.getForeverDenied());
            }
        }

        for (ResponseCallback responseCallback : responseCallbacks) {
            responseCallback.onResponse(permissionResult);
        }
    }

    /**
     * We want to only request given permissions
     * If we do not call this method, the library will find all needed permissions to ask from manifest
     *
     * @see
     */
    public RuntimePermission request(final List<String> permissions) {
        if (permissions != null) {
            permissionsToRequest.clear();
            permissionsToRequest.addAll(permissions);
        }
        return this;
    }

    /**
     * We want to only request given permissions
     *
     * @see
     */
    public RuntimePermission request(final String... permissions) {
        if (permissions != null) {
            return this.request(Arrays.asList(permissions));
        } else {
            return this;
        }
    }

    public RuntimePermission onResponse(final ResponseCallback callback) {
        if (callback != null) {
            responseCallbacks.add(callback);
        }
        return this;
    }

    public RuntimePermission onResponse(final PermissionListener permissionListener) {
        if (permissionListener != null) {
            permissionListeners.add(permissionListener);
        }
        return this;
    }

    public RuntimePermission onAccepted(final AcceptedCallback callback) {
        if (callback != null) {
            acceptedCallbacks.add(callback);
        }
        return this;
    }

    public RuntimePermission onDenied(final DeniedCallback callback) {
        if (callback != null) {
            deniedCallbacks.add(callback);
        }
        return this;
    }

    public RuntimePermission onForeverDenied(final ForeverDeniedCallback callback) {
        if (callback != null) {
            foreverDeniedCallbacks.add(callback);
        }
        return this;
    }

    public void ask(ResponseCallback responseCallback) {
        onResponse(responseCallback)
                .ask();
    }

    public void ask(PermissionListener permissionListener) {
        onResponse(permissionListener)
                .ask();
    }

    /**
     * If we request permission using .request(names), we only ask them
     * If not, this lib will search needed permissions from Manifest
     */
    private List<String> findNeededPermissions(Context context) {
        if (permissionsToRequest.isEmpty()) {
            return PermissionManifestFinder.findNeededPermissionsFromManifest(context);
        } else {
            return permissionsToRequest;
        }
    }

    /**
     * Ask for the permission. Which permission? Anything you register on your manifest that needs it.
     * It is safe to call this every time without querying `shouldAsk`.
     * In case you call `ask` without any permission, the method returns.
     */
    public void ask() {
        final FractionAbility ability = activityReference.get();
        if (ability == null || ability.isTerminating()) {
            return;
        }


        //retrieve permissions we want
        final List<String> permissions = findNeededPermissions(ability);

        // No need to ask for permissions on API levels below Android Marshmallow

        if (permissions.isEmpty() || arePermissionsAlreadyAccepted(ability, permissions)) {
            onAllAccepted(permissions);
        } else {
            final Optional<Fraction> optional = ability
                    .getFractionManager()
                    .getFractionByTag(TAG);
            if (optional.isPresent()) {
                PermissionFraction oldFragment = (PermissionFraction) optional.get();
                oldFragment.setListener(listener);
            } else {

                final PermissionFraction newFragment = PermissionFraction.newInstance(permissions);

                newFragment.setListener(listener);

                TaskDispatcher taskDispatcher = ability.getUITaskDispatcher();
                taskDispatcher.delayDispatch(new Runnable() {
                    @Override
                    //add方法需要int类型，暂时未解决，编译不报错
                    public void run() {
                        System.out.println("---------into run---------");
                        ability.getFractionManager()
                                .startFractionScheduler()
                                .add(0, newFragment, TAG)
                                .submit();
                    }
                }, 1000);

            }
        }
    }

    private boolean arePermissionsAlreadyAccepted(Context context, final List<String> permissions) {
        for (String permission : permissions) {
            final int permissionState = context.verifySelfPermission(permission);
            if (permissionState == PERMISSION_DENIED) {
                return false;
            }

        }
        return true;
    }

    private void onAllAccepted(final List<String> permissions) {
        onReceivedPermissionResult(permissions, null, null);
    }

    public void askAgain() {

    }
}
